#pragma once
#include "CTokens.h"
#include "CFileSystem.h"
#include "CScripts.h"

extern class CScriptEngine			ScriptEngine;
extern class CConsoleScripts			ConsoleScripts;

class CSCRamblScript : public CScript
{
public:
	CSCRamblScript(const char * szFilePath) : CScript(szFilePath)
	{
		if(IsParseNeeded()) Build();
	}
};

struct ScriptVar
{
	union
	{
		int				nVal;
		float			fVal;
	};
};

class CConsoleScript : public CIdentifier
{
	friend class CConsoleScripts;

	CSCRamblScript					*	m_pSCRamblScript;
	CFileData						*	m_pSourceFile;
	CFileData						*	m_pCompiledFile;
	void								*	m_pData;
	void								*	m_pCurrentData;
	void								*	m_Stack[8];
	short								m_nStackIdx;
	int									m_nWakeTime;
	short								m_nLogOp;
	bool									m_bNot;
	bool									m_bConditionResult;
	bool									m_bIsActive;
	ScriptVar							m_LocalStorage[16];
	ScriptVar							m_TimerStorage[2];

	CConsoleScript(const char * szName) : CIdentifier(szName),
		m_pSCRamblScript(nullptr), m_pData(nullptr), m_pCurrentData(nullptr),
		m_Stack(), m_nStackIdx(0), m_nWakeTime(0), m_nLogOp(0), m_bNot(false),
		m_bConditionResult(false), m_bIsActive(false), m_LocalStorage(),
		m_TimerStorage()
	{
	}

template<class T>
	inline T ReadLVar(int nIndex)					{return m_LocalStorage[nIndex];}
//template<class T>
	//inline T GetVarPtr(size_t dwOffset)			{return (T*)((BYTE*)m_LocalStorage + dwOffset);}
template<class T>
	inline T * GetLVarPtr(int nIndex)				{return (T*)&m_LocalStorage[nIndex];}
template<class T>
	inline T * GetCurrentPtr(size_t dwOffset=0)		{return (T*)((BYTE*)m_pCurrentData + dwOffset);}
template<class T>
	inline T * GetOffsetPtr(size_t dwOffset)		{return (T*)((BYTE*)m_pData + dwOffset);}
	
template<class T>
	inline T * GetDataPtr()							{return (T*)m_pCurrentData;}
template<class T>
	inline T ReadNoInc()							{T *v = (T*)m_pCurrentData; return *T;}
template<class T>
	inline T Read()
	{
		T *p = (T*)m_pCurrentData;
		T *v = p;
		++p;
		m_pCurrentData = p;
		return *v;
	}

template<class T>
	T ReadValue()
	{
		T v = 0;
		return v;
	}

public:
	inline void Activate(bool b=true)
	{
		if(m_bIsActive = m_pData && b) ReadScript();
	}

	inline void SetSourceData(CFileData * pData)
	{
		m_pSourceFile = pData;
		m_pSCRamblScript = new CSCRamblScript(pData->GetFilePath());
	}
	inline void SetCompiledData(CFileData * pData)
	{
		m_pCompiledFile = pData;
		Activate();
	}

	const char * ReadString(char * szStrOut=nullptr, int nSize=16)
	{
		char dt = Read<char>();
		int i;
		int nVarOffset=0;
		switch(dt)
		{
		case DT_NULL:
			return nullptr;
		case DT_INT32:
			return (const char *)Read<int>();
		case DT_LABEL:
			if(!szStrOut) return GetOffsetPtr<const char>(Read<DWORD>());
			szncpy(szStrOut, GetOffsetPtr<const char>(Read<DWORD>()), nSize);
			return szStrOut;
		case DT_TEXT_LABEL:
			if(!szStrOut) return GetCurrentPtr<const char>();
			szncpy(szStrOut, GetCurrentPtr<const char>(), 8);
			return szStrOut;
		case DT_TEXT_LABEL_16:
			if(!szStrOut) return GetCurrentPtr<const char>();
			szncpy(szStrOut, GetCurrentPtr<const char>(), 16);
			return szStrOut;
		case DT_STRING:
			i = Read<char>();
			if(nSize > i) nSize = i;
			if(!szStrOut) return GetCurrentPtr<const char>();
			szncpy(szStrOut, GetCurrentPtr<const char>(), nSize);
			return szStrOut;
		case DT_VAR_TEXT_LABEL_ARRAY:
		case DT_VAR_STRING_ARRAY:
		case DT_LVAR_TEXT_LABEL_ARRAY:
		case DT_LVAR_STRING_ARRAY:
			//
		case DT_VAR_TEXT_LABEL:
		case DT_VAR_STRING:
		case DT_LVAR_TEXT_LABEL:
		case DT_LVAR_STRING:
			if(!szStrOut) return GetLVarPtr<const char>(Read<WORD>());
			szncpy(szStrOut, GetLVarPtr<const char>(Read<WORD>()), nSize);
			return szStrOut;
		}
		THROW("Unacceptable data type %02X", dt);
		return nullptr;
	}

	void Process();
	void ReadScript();
};

class CConsoleScripts
{
	CList<CConsoleScript>			m_Scripts;

public:
	// Finds or creates a script
	inline CConsoleScript * Request(const char * szName)				{return Request(szName, GenerateHash(szName));}
	CConsoleScript * Request(const char * szName, DWORD dwHash)
	{
		auto pConsoleScript = m_Scripts.Get(dwHash);

		while(pConsoleScript && !pConsoleScript->IsNamed(szName, dwHash))
			pConsoleScript = m_Scripts.Next();

		if(!pConsoleScript)
		{
			pConsoleScript = new CConsoleScript(szName);
			m_Scripts.Add(pConsoleScript->GetHash(), pConsoleScript);
		}
		return pConsoleScript;
	}
};

class CScriptEngine// : public CConsoleCommands
{
	CDirectory				*	m_ScriptsDir;
	CLListEx<CConsoleScript *>	m_Scripts;

public:
	CScriptEngine() : m_ScriptsDir(new CDirectory("scripts", "*.scc"))
	{
		m_ScriptsDir->FindFiles("scripts\\*.scs");
	}

	// Initialises scripts
	void Init()
	{
		for(auto pLink = m_ScriptsDir->Begin(); pLink != m_ScriptsDir->Bad(); ++pLink)
		{
			if(pLink->IsExtension("scc"))
			{
				auto pScript = ConsoleScripts.Request(pLink->GetFilePath());
				pScript->SetCompiledData(*pLink);
				m_Scripts.Add(pScript);
				Print("Found .scc file");
			}
			else if(pLink->IsExtension("scs"))
			{
				CompileScript(*pLink);
				Print("Found .scs file");
			}
		}
	}
	void CompileScript(CFileData * pFileData);
};